package com.uzhie.jt808;

import com.uzhie.jt808.msg.JT808MsgBean;
import com.uzhie.jt808.util.ByteUtil;
import com.uzhie.jt808.util.JT808ProtocolUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.apache.log4j.Logger;

/**
 * Created by TangFei on 2018/8/9.
 * 808 协议编码器
 */
public class JT808ByteEncoder extends MessageToByteEncoder<JT808MsgBean> {

    private Logger logger = Logger.getLogger(getClass());
    private JT808MsgBean msgBean;
    private ByteBuf byteBuf;

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, JT808MsgBean msgBean, ByteBuf byteBuf) throws Exception {
        logger.trace("消息ID：" + msgBean.getHeader().getHexMsgID() + " SIM：" + msgBean.getHeader().getSimNum() + " 消息流水：" + msgBean.getHeader().getFlowNum());
        this.msgBean = msgBean;
        this.byteBuf = byteBuf;
        build808SendBytes();
    }

    /**
     * 创建808发送字节数组
     * <p>
     * param msgBean 808协议对象
     *
     * @return 808协议字节数组
     * @throws Exception
     */
    private void build808SendBytes() throws Exception {
        // 写入分隔符
        byteBuf.writeByte(JT808Constant.SPLIT_SYMBOL);
        byteBuf.markReaderIndex();
        byteBuf.markWriterIndex();
        // 写入消息头
        build808HeaderBytes(msgBean, byteBuf);
        // 写入消息体
        byteBuf.writeBytes(msgBean.getBodyBytes());
        // 写入校验码
        byteBuf.readerIndex(1);
        byte[] headerAndBody = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(headerAndBody, 0, headerAndBody.length);
        byteBuf.resetReaderIndex();
        int checkCode = ByteUtil.parseCheckCode(headerAndBody, headerAndBody.length);
        byteBuf.writeBytes(JT808ProtocolUtil.parseInt2Byte(checkCode));
        // 特殊字节转义
        byteBuf.readerIndex(1);
        byte[] contentBytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(contentBytes, 0, contentBytes.length);
        byteBuf.resetReaderIndex();
        // 转义后的字节数组
        byte[] escapeEncodeBytes = JT808ProtocolUtil.doEscapeEncode(contentBytes);
        byteBuf.resetWriterIndex();
        byteBuf.writeBytes(escapeEncodeBytes);
        // 写入分隔符
        byteBuf.writeByte(JT808Constant.SPLIT_SYMBOL);
//        for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
//            logger.trace("消息ID：" + msgBean.getHeader().getHexMsgID() + " 方法调用堆栈：" + element.toString());
//        }
        logger.trace("消息ID：" + msgBean.getHeader().getHexMsgID() + "发送给终端的字节:" + ByteBufUtil.hexDump(byteBuf));
    }

    /**
     * 创建发送消息头
     *
     * @param msgBean
     * @param byteBuf
     * @return ByteBuf
     */
    private ByteBuf build808HeaderBytes(JT808MsgBean msgBean, ByteBuf byteBuf) {
        // 消息ID
        byteBuf.writeBytes(JT808ProtocolUtil.parseInt2Word(msgBean.getHeader().getMsgID()));
        // 消息体属性
        byteBuf.writeBytes(JT808ProtocolUtil.parseInt2Word(JT808ProtocolUtil.generateMsgBodyProps(msgBean.getBodyBytes().length, msgBean.getHeader().getEncrypt(), msgBean.getHeader().isHasSubPakage(), 0)));
        // 终端手机号
        byteBuf.writeBytes(ByteUtil.stringToBCDByteArray(msgBean.getHeader().getSimNum()));
        // 流水号
        byteBuf.writeBytes(JT808ProtocolUtil.parseInt2Word(msgBean.getHeader().getFlowNum()));
        // 如果有分包 则追加消息包封装项
        if (msgBean.getHeader().isHasSubPakage()) {
            // 消息总包数
            byteBuf.writeBytes(JT808ProtocolUtil.parseInt2Word(msgBean.getHeader().getTotalSubPackage()));
            // 包序号
            byteBuf.writeBytes(JT808ProtocolUtil.parseInt2Word(msgBean.getHeader().getNumSubPackage()));
        }
        return byteBuf;
    }

    /**
     * 拼接消息头 消息体 校验码 并将结果集转义
     *
     * @param headerAndBody 消息头和消息体
     * @param checkCode     校验码
     * @return 808协议字节数组
     * @throws Exception
     */
    private byte[] doEncode(byte[] headerAndBody, int checkCode) throws Exception {
        // 计算并填充校验码
        byte[] bytes = ByteUtil.concatAll(headerAndBody, JT808ProtocolUtil.parseInt2Byte(checkCode));
        // 转义
        byte[] escapeEncode = JT808ProtocolUtil.doEscapeEncode(bytes);
        // 拼接
        byte[] result = ByteUtil.concatAll(new byte[]{JT808Constant.SPLIT_SYMBOL}, escapeEncode, new byte[]{JT808Constant.SPLIT_SYMBOL});
        return result;
    }

}
